package com.zoewin.zephyr.reportjasper.factory.creator;

import com.deepoove.poi.resolver.RunEdge;
import com.deepoove.poi.util.StyleUtils;
import com.zoewin.zephyr.reportjasper.entity.ReportEntity;
import com.zoewin.zephyr.reportjasper.plugins.ReflectionUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by WQL on 2020/8/5
 * CEASE TO STRUGGLE AND YOU CEASE TO LIVE
 */
public class PoiPrintCreator {

    public static XWPFDocument getPoiPrint(ReportEntity reportEntity, Map map) {
        XWPFDocument document = reportEntity.getDocument();

        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            changeParagraph(paragraph, map);
        }
        List<XWPFTable> tables = document.getTables();
        for (XWPFTable table : tables) {
            changeTableMap(table, map);
        }
        return document;
    }

    public static XWPFDocument getPoiPrint(ReportEntity reportEntity, Map map, Map<String, List<Map>> mapList) {
        XWPFDocument document = reportEntity.getDocument();
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            changeParagraph(paragraph, map);
        }
        for (int i = 0; i < document.getTables().size(); i++) {
            XWPFTable table = document.getTables().get(i);
            changeTableList(table, mapList);
            changeTableMap(table, map);
        }
        return document;
    }

    private static void changeTableMap(XWPFTable table, Map map) {
        List<XWPFTableRow> rows = table.getRows();
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
                for (XWPFParagraph cellParagraph : cellParagraphs) {
                    changeParagraph(cellParagraph, map);
                }
                for (XWPFTable cellTable : cell.getTables()) {
                    changeTableMap(cellTable, map);
                }
            }
        }
    }

    private static void changeTableList(XWPFTable table, Map<String, List<Map>> mapList) {
        //循行模板所在位置
        int templateRowIndex = 0;
        String listTag = "";
        boolean flag = true;
        List<XWPFTableRow> rows = table.getRows();
        for (int i = 0; i < rows.size() && flag; i++) {
            List<XWPFTableCell> cells = rows.get(i).getTableCells();
            for (int i1 = 0; i1 < cells.size() && flag; i1++) {
                List<XWPFParagraph> paragraphs = cells.get(i1).getParagraphs();
                for (int i2 = 0; i2 < paragraphs.size() && flag; i2++) {
                    if (findListTag(paragraphs.get(i2))) {
                        templateRowIndex = i + 1;
                        String text = paragraphs.get(i2).getText();
                        int b = text.indexOf("$F{")+3;
                        int e = text.indexOf("}");
                        listTag =text.substring(b,e);
                        flag = false;
                        removeList(paragraphs.get(i2));
                    }
                }
            }
        }
        //获取循环行模板
        XWPFTableRow templateRow = table.getRow(templateRowIndex);

        Set<Map.Entry<String, List<Map>>> entries = mapList.entrySet();
        Iterator<Map.Entry<String, List<Map>>> iterator = entries.iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, List<Map>> next = iterator.next();
            String key = next.getKey();
            List<Map> list = next.getValue();
            if (key.equalsIgnoreCase(listTag)) {
                for (Map map : list) {
                    int insertPosition = templateRowIndex++;
                    //插入新的行
                    table.insertNewTableRow(insertPosition);
                    setTableRow(table, templateRow, insertPosition);

                    XmlCursor newCursor = templateRow.getCtRow().newCursor();
                    newCursor.toPrevSibling();
                    XmlObject object = newCursor.getObject();
                    XWPFTableRow nextRow = new XWPFTableRow((CTRow)object, table);
                    setTableRow(table, nextRow, insertPosition);

                    for (int i = 0; i < table.getRow(insertPosition).getTableCells().size(); i++) {
                        XWPFTableCell cell = table.getRow(insertPosition).getTableCells().get(i);
                        List<XWPFParagraph> paragraphs = cell.getParagraphs();
                        for (XWPFParagraph paragraph : paragraphs) {
                            changeList(paragraph,map);
                        }
                    }
                }
            }
        }
        table.removeRow(templateRowIndex);
    }

    private static void setTableRow(XWPFTable table, XWPFTableRow templateRow, int pos){
        List<XWPFTableRow> rows = (List) ReflectionUtils.getValue("tableRows", table);
        rows.set(pos, templateRow);
        table.getCTTbl().setTrArray(pos, templateRow.getCtRow());
    }


    private static void changeParagraph(XWPFParagraph paragraph, Map<String, String> map) {
        String regex = "\\$P\\{(\u0000|#|@|\\*|\\+)?[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(paragraph.getText());
        List<Pair<RunEdge, RunEdge>> pairs = new ArrayList();
        while (matcher.find()) {
            pairs.add(ImmutablePair.of(new RunEdge(matcher.start(), matcher.group()), new RunEdge(matcher.end(), matcher.group())));
        }
        List<XWPFRun> runs = paragraph.getRuns();
        if (!pairs.isEmpty()) {
            refactorRuns(runs, pairs, paragraph);
        }
        for (XWPFRun run : runs) {
            Matcher m = pattern.matcher(run.text());
            if (m.find()) {
                String s = map.get(run.text().replace("$P{", "").replace("}", ""));
                run.setText(StringUtils.hasText(s) ? s : "", 0);
            }
        }
    }

    private static boolean findListTag(XWPFParagraph paragraph) {
        boolean flag = false;
        String regex = "\\$F\\{(\u0000|#|@|\\*|\\+)?[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(paragraph.getText());

        while (matcher.find()) {
            flag = true;
            break;
        }
        return flag;
    }

    private static void changeList(XWPFParagraph paragraph, Map<String, String> map) {
        String regex = "\\[(\u0000|#|@|\\*|\\+)?[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*\\]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(paragraph.getText());
        List<Pair<RunEdge, RunEdge>> pairs = new ArrayList();
        while (matcher.find()) {
            pairs.add(ImmutablePair.of(new RunEdge(matcher.start(), matcher.group()), new RunEdge(matcher.end(), matcher.group())));
        }
        List<XWPFRun> runs = paragraph.getRuns();
        if (!pairs.isEmpty()) {
            refactorRuns(runs, pairs, paragraph);
        }
        for (XWPFRun run : runs) {
            Matcher m = pattern.matcher(run.text());
            if (m.find()) {
                String s = map.get(run.text().replace("[", "").replace("]", ""));
                run.setText(StringUtils.hasText(s) ? s : "", 0);
            }
        }
    }

    private static void removeList(XWPFParagraph paragraph) {
        String regex = "\\$F\\{(\u0000|#|@|\\*|\\+)?[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(paragraph.getText());
        List<Pair<RunEdge, RunEdge>> pairs = new ArrayList();
        while (matcher.find()) {
            pairs.add(ImmutablePair.of(new RunEdge(matcher.start(), matcher.group()), new RunEdge(matcher.end(), matcher.group())));
        }
        List<XWPFRun> runs = paragraph.getRuns();
        if (!pairs.isEmpty()) {
            refactorRuns(runs, pairs, paragraph);
        }
        for (int i = 0; i < runs.size(); i++) {
            Matcher m = pattern.matcher(runs.get(i).text());
            if (m.find()) {
                runs.get(i).setText("",0);
            }
        }
    }

    private static void refactorRuns(List<XWPFRun> runs, List<Pair<RunEdge, RunEdge>> pairs, XWPFParagraph paragraph) {
        int size = runs.size();
        int cursor = 0;
        int pos = 0;
        Pair<RunEdge, RunEdge> pair = (Pair) pairs.get(pos);
        RunEdge startEdge = (RunEdge) pair.getLeft();
        RunEdge endEdge = (RunEdge) pair.getRight();
        int start = startEdge.getAllEdge();
        int end = endEdge.getAllEdge();
        for (int i = 0; i < size; ++i) {
            XWPFRun run = (XWPFRun) runs.get(i);
            String text = run.text();
            if (null == text) {
                cursor += run.toString().length();
            } else {
                if (text.length() + cursor < start) {
                    cursor += text.length();
                } else {
                    for (int offset = 0; offset < text.length(); ++offset) {
                        if (cursor + offset == start) {
                            startEdge.setRunPos(i);
                            startEdge.setRunEdge(offset);
                            startEdge.setText(text);
                        }
                        if (cursor + offset == end - 1) {
                            endEdge.setRunPos(i);
                            endEdge.setRunEdge(offset);
                            endEdge.setText(text);
                            if (pos == pairs.size() - 1) {
                                break;
                            }
                            ++pos;
                            pair = (Pair) pairs.get(pos);
                            startEdge = (RunEdge) pair.getLeft();
                            endEdge = (RunEdge) pair.getRight();
                            start = startEdge.getAllEdge();
                            end = endEdge.getAllEdge();
                        }
                    }
                    cursor += text.length();
                }
            }
        }
        if (pairs.isEmpty()) {
            return;
        } else {
            List<XWPFRun> templateRuns = new ArrayList();
            int size1 = pairs.size();
            for (int n = size1 - 1; n >= 0; --n) {
                Pair<com.deepoove.poi.resolver.RunEdge, com.deepoove.poi.resolver.RunEdge> runEdgePair = (Pair) pairs.get(n);
                RunEdge startEdge1 = runEdgePair.getLeft();
                RunEdge endEdge1 = runEdgePair.getRight();
                int startRunPos = startEdge1.getRunPos();
                int endRunPos = endEdge1.getRunPos();
                int startOffset = startEdge1.getRunEdge();
                int endOffset = endEdge1.getRunEdge();
                String startText = ((XWPFRun) runs.get(startRunPos)).text();
                String endText = ((XWPFRun) runs.get(endRunPos)).text();
                String extra;
                XWPFRun extraRun;
                if (endOffset + 1 >= endText.length()) {
                    if (startRunPos != endRunPos) {
                        paragraph.removeRun(endRunPos);
                    }
                } else {
                    extra = endText.substring(endOffset + 1, endText.length());
                    if (startRunPos == endRunPos) {
                        extraRun = paragraph.insertNewRun(endRunPos + 1);
                        StyleUtils.styleRun(extraRun, (XWPFRun) runs.get(endRunPos));
                        buildExtra(extra, extraRun);
                    } else {
                        extraRun = (XWPFRun) runs.get(endRunPos);
                        buildExtra(extra, extraRun);
                    }
                }

                for (int m = endRunPos - 1; m > startRunPos; --m) {
                    paragraph.removeRun(m);
                }

                if (startOffset <= 0) {
                    XWPFRun templateRun = (XWPFRun) runs.get(startRunPos);
                    templateRun.setText(startEdge1.getTag(), 0);
                    templateRuns.add(runs.get(startRunPos));
                } else {
                    extra = startText.substring(0, startOffset);
                    extraRun = (XWPFRun) runs.get(startRunPos);
                    buildExtra(extra, extraRun);
                    XWPFRun templateRun = paragraph.insertNewRun(startRunPos + 1);
                    StyleUtils.styleRun(templateRun, extraRun);
                    templateRun.setText(startEdge1.getTag(), 0);
                    templateRuns.add(runs.get(startRunPos + 1));
                }
            }
        }
    }

    private static void buildExtra(String extra, XWPFRun extraRun) {
        extraRun.setText(extra, 0);
    }
}
